package main

/* by zy
  - 2025.11.31
    parseArgs() *CLI : 分析命令行参数，返回结构化的配置信息
    (cfg *CLI) Validate() : 子命令的参数检查
    showUsage() 输出命令行用法
  - 2025.11.05
    增加子命令 -check -fqdn <domain> [-days <N>] 用于检查域名使用中的证书状态
    为了支持通配符证书, 以下子命令不再由 -cn 提供域名信息:
     / * 子命令 -set -cn <common name>  -cert-id <ID> 调整为 -set -fqdn <domain name> -cert-id <ID>
     \ * 子命令 -upload 携带 -deploy 标记时，增加必需参数 -fqdn <domain name>
*/

import (
  "flag"
  "fmt"
  "os"
)

// 从命令行参数中提取配置信息
func parseArgs() *CLI {
  //替换为自定义的 showUsage() 函数
  flag.Usage = showUsage

  // 全局参数
  accessKey := flag.String("access-key", "", "Access key (or env QINIU_ACCESS_KEY)")
  secretKey := flag.String("secret-key", "", "Secret key (or env QINIU_SECRET_KEY)")

  // 子命令标志
  cmds := map[string]*bool{
    "list":     flag.Bool("list", false, "List deployed certs"),
    "upload":   flag.Bool("upload", false, "Upload new cert"),
    "download": flag.Bool("download", false, "Download cert"),
    "delete":   flag.Bool("delete", false, "Delete cert"),
    "set":      flag.Bool("set", false, "Update cert binding"),
    "check":    flag.Bool("check", false, "Check certificate state (Valid|Expiring|Expired)"),
  }

   // 子命令参数
  certFile := flag.String("cert", "", "Certificate file")
  keyFile := flag.String("key", "", "Key file")
  name := flag.String("name", "", "Name")
  cn := flag.String("cn", "", "Common name")
  fqdn := flag.String("fqdn", "", "Domain name")
  days := flag.Int("days", DefaultExpirationWindow, "Days left before expiring (default: 14)") 
  certID := flag.String("cert-id", "", "Cert ID")
  deploy := flag.Bool("deploy", false, "Deploy optional")
  verbose := flag.Bool("verbose", false, "Verbose output")
  expired := flag.Bool("expired", false, "Expired (used with -cn)")
  flag.Parse()
 
  // fallback 环境变量
  if *accessKey == "" {
    *accessKey = os.Getenv("QINIU_ACCESS_KEY")
  }
  if *secretKey == "" {
    *secretKey = os.Getenv("QINIU_SECRET_KEY")
  }
  if *accessKey == "" || *secretKey == "" {  //参数和环境变量都缺少必要的 access-key / secret-key
    _, _ = fmt.Fprintf(os.Stderr, "error: missing credentials, using -access-key/-secret-key (or env QINIU_ACCESS_KEY / QINIU_SECRET_KEY)\n")
    showUsage()
    os.Exit(ExitUsageError)
  }

  // 检查子命令是否冲突, 子命令应当有且仅有一个
  subCmd := SubUnknown
  for name, ptr := range cmds {
    if ! *ptr { // 命令行中未指定的参数(即相应变量值为 false)
      continue
    }
    if subCmd != SubUnknown {
      _, _ = fmt.Fprintf(os.Stderr, "error: only ONE sub-command is allowed\n")
      showUsage()
      os.Exit(ExitUsageError)
    }
    switch name {
    case "list":
      subCmd = SubList
    case "upload":
      subCmd = SubUpload
    case "download":
      subCmd = SubDownload
    case "delete":
      subCmd = SubDelete
    case "set":
      subCmd = SubSet
    case "check":
      subCmd = SubCheck
    }
  }

  if subCmd == SubUnknown { // 未指定任何子命令
    _, _ = fmt.Fprintf(os.Stderr, "error: a sub-command is required\n")
    showUsage()
    os.Exit(ExitUsageError)
  }
  
  cfg := CLI {
    AccessKey: *accessKey,
    SecretKey: *secretKey,
    SubCmd:    subCmd,
    CertFile:  *certFile,
    KeyFile:   *keyFile,
    CertName:  *name,
    CN:        *cn,
    CertID:    *certID,
    Deploy:    *deploy,
    Verbose:   *verbose,
    Expired:   *expired,
    Fqdn:      *fqdn,
    Days:      *days,
  }
  return &cfg
}

// 子命令参数检查
func (cfg *CLI) Validate() {
  hasError := false
  switch(cfg.SubCmd) {
  case SubUpload:
    if cfg.CertFile == "" {
      hasError = true
      _, _ = fmt.Fprintf(os.Stderr, "-cert <cert file> is required\n")
    }
    if cfg.KeyFile == "" {
      hasError = true
      _, _ = fmt.Fprintf(os.Stderr, "-key <key file> is required\n")
    }
    if cfg.CertName == "" {
      hasError = true
      _, _ = fmt.Fprintf(os.Stderr, "-name <cert name> is required\n")
    }
    if cfg.CN == "" {
      hasError = true
      _, _ = fmt.Fprintf(os.Stderr, "-cn <common name> is required\n")
    }
    if cfg.Deploy && cfg.Fqdn == "" {
      hasError = true
      _, _ = fmt.Fprintf(os.Stderr, "-fqdn <domain name> is required for -deploy \n")
    }
  case SubDownload:
    if cfg.CertID == "" {
      hasError = true
      _, _ = fmt.Fprintf(os.Stderr, "-cert-id is required\n");
    }
  case SubDelete:
    if cfg.CertID == "" && !(cfg.CN != "" && cfg.Expired) {
      hasError = true
      _, _ = fmt.Fprintf(os.Stderr, "-cert-id <ID> or -cn <CN> -expired is required\n")
    }
  case SubSet:
    if cfg.CertID == "" || cfg.Fqdn == "" {
      hasError = true
      _, _ = fmt.Fprintf(os.Stderr, "-cert-id <ID> and -fqdn <domain name> are required\n")
    }
  case SubCheck:
    if cfg.Fqdn == "" {
      hasError = true
      _, _ = fmt.Fprintf(os.Stderr, "-fqdn <domain name> is required\n")
    }
  }
  if hasError {
    showUsage()
    os.Exit(ExitUsageError)
  }
}

// 输出帮助信息
func showUsage() {
    _, _ = fmt.Printf(`Usage:

  %s -access-key <ak> -secret-key <sk> <sub-command> [options]

Global Options:
  -access-key           Required, Access key (or env QINIU_ACCESS_KEY)
  -secret-key           Required, Secret key (or env QINIU_SECRET_KEY)

sub-commands:
  -list                 List deployed certs
    -verbose            Optional verbose output

  -list [-verbose]

  -upload               Upload new cert
    -cert <file>        Required, file read from
    -key <file>         Required, file read from 
    -name <name>        Required
    -cn <common name>   Required
    -deploy             Optional
    -fqdn <domain name> Required if -deploy

  -upload -cert <cert file> -key <key file> -name <name> -cn <common name> [-deploy -fqdn <domain name>]

  -download             Download cert
    -cert-id <id>       Required
    -cert <file>        Optional, file save to
    -key <file>         Optional, file save to
    -verbose            Optional
  
  -download -cert-id <id> [-key <file>] [-cert <file>] [-verbose]

  -delete               Delete cert
    -cert-id <id>       OR (-expired -cn <common name>)
    -expired            Optional (used with -cn)
    -cn <common name>   Optional (used with -expired)

  -delete { -cert-id <id> | -expired -cn <common name> }

  -set                  Update cert binding
    -cert-id <id>       Required
    -fqdn <domain name> Required

  -set -fqdn <domain name> -cert-id <id>

  -check                Check certificate state (Valid|Expiring|Expired)
    -fqdn <domain>      Required
    -days <N>           Optional (days left before expiring, default: %d)

  -check -fqdn <domain name> [-days <N>]

Environment Variables:
  QINIU_ACCESS_KEY      Fallback for -access-key
  QINIU_SECRET_KEY      Fallback for -secret-key %s`, os.Args[0], DefaultExpirationWindow, "\n")
}
